package com.rybbaby.tss.tasks.core.web.servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Predicate;
import java.util.stream.Stream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.rybbaby.tss.tasks.core.bean.TaskClassBean;
import com.rybbaby.tss.tasks.core.bean.TaskMethodBean;
import com.rybbaby.tss.tasks.core.utils.PropertiesReadUtil;
import com.rybbaby.tss.tasks.core.utils.SpringApplicationContextUtil;
import com.rybbaby.tss.tasks.core.annotation.TaskClass;
import com.rybbaby.tss.tasks.core.annotation.TaskMethod;
import net.sf.json.JSONObject;
import org.apache.commons.io.Charsets;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.util.IOUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Scheduled;

/**
 * @description 任务调度管理
 * 
 * @author 谢进伟
 * 
 * @createDate 2016年10月13日 下午2:48:45
 */
public class TaskManagerServlet extends HttpServlet {
	
	private static final long serialVersionUID = 1L;
	
	private static List<TaskClassBean> tcbs = new ArrayList<TaskClassBean>();
	private static List<String> runingTaskIds = new LinkedList<String>();
	
	public void doGet(HttpServletRequest request , HttpServletResponse response) throws ServletException , IOException {
		this.doPost(request , response);
	}
	
	public void doPost(HttpServletRequest request , HttpServletResponse response) throws ServletException , IOException {
		String cmd = request.getParameter("cmd");
		cmd = StringUtils.isBlank(cmd) ? "" : cmd;
		switch(cmd){
			case "exec":// 执行任务调度方法
				execTaskMethod(request , response);
				break;
			case "getRunlingTasks":
				response.getWriter().print(".status_" + StringUtils.join(runingTaskIds , ",.status_"));
				break;
			case "showLog":
				realtimeShowLog(request , response);
				break;
			case "reload":// 重新加载任务的调度类
				tcbs.clear();
			default:// 显示任务调度任务列表
				if(tcbs.isEmpty()) {
					loadTaksClass();
				}
				request.setAttribute("tcbs" , tcbs);
				request.setAttribute("runingTaskIds" , runingTaskIds);
				request.getRequestDispatcher("../taskManager.jsp").forward(request , response);
				break;
		}
	}
	
	/**
	 * 实时输出日志信息
	 * 
	 * @param lastTimeFileSize
	 *            上次文件大小
	 * @throws IOException
	 */
	public void realtimeShowLog(HttpServletRequest request , HttpServletResponse response) throws IOException {
		String lastTimeFileSizeStr = request.getParameter("lastTimeFileSize");
		response.setCharacterEncoding(Charsets.UTF_8.toString());
		PrintWriter out = null;
		RandomAccessFile randomFile = null;
		Map<String , Object> result = new HashMap<String , Object>();
		try {
			String log4jPath = PropertiesReadUtil.getContextProperty("log.path");
			File logFile = new File(log4jPath);
			if(!logFile.exists()) {
				result.put("lastTimeFileSize" , 0);
				result.put("content" , "log file is not find!");
			} else {
				randomFile = new RandomAccessFile(logFile , "rw");
				int maxShowLen = 1024 * 10;
				long lastTimeFileSize = StringUtils.isBlank(lastTimeFileSizeStr) ? ((randomFile.length() - maxShowLen) < 0 ? 0 : (randomFile.length() - maxShowLen)) : Integer.parseInt(lastTimeFileSizeStr);
				if(lastTimeFileSize != 0) {
					// 获得变化部分的
					randomFile.seek(lastTimeFileSize);
				}
				StringBuffer sb = new StringBuffer();
				String tmp = "";
				long len = 0;
				boolean isSet = false;
				while ((tmp = randomFile.readLine()) != null) {
					sb.append(new String(tmp.getBytes(Charsets.ISO_8859_1.toString())));
					sb.append("\n");
					len = sb.toString().getBytes().length;
					if(len > maxShowLen) {
						lastTimeFileSize += len;
						isSet = true;
						break;
					}
				}
				if(!isSet) {
					lastTimeFileSize = randomFile.length();
				}
				result.put("lastTimeFileSize" , lastTimeFileSize);
				result.put("content" , sb.toString());
			}
			out = response.getWriter();
			out.print(JSONObject.fromObject(result));
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(randomFile);
			IOUtils.closeQuietly(out);
		}
	}
	
	/**
	 * 执行任务调度方法
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	private void execTaskMethod(HttpServletRequest request , HttpServletResponse response) throws IOException {
		response.setCharacterEncoding(Charsets.UTF_8.toString());
		String className = request.getParameter("className");
		String methodName = request.getParameter("methodName");
		Map<String , Object> result = new HashMap<String , Object>();
		StringBuffer runId = new StringBuffer();
		PrintWriter writer = null;
		String resMsg = "执行成功!";
		int resCode = 200;
		try {
			writer = response.getWriter();
			runId.append(className).append("_").append(methodName);
			if(StringUtils.isNotBlank(className)) {
				Stream<TaskClassBean> filter_taskClass = tcbs.parallelStream().filter(new Predicate<TaskClassBean>() {
					
					@Override
					public boolean test(TaskClassBean t) {
						String className2 = t.getClassName();
						return className2 != null && className2.toString().equals(className);
					}
					
				});
				if(filter_taskClass.isParallel()) {
					Optional<TaskClassBean> findFirst_taskClass = filter_taskClass.findFirst();
					if(findFirst_taskClass != null && findFirst_taskClass.isPresent()) {
						TaskClassBean tcb = findFirst_taskClass.get();
						List<TaskMethodBean> taskMethods = tcb.getTaksMethods();
						Stream<TaskMethodBean> filter_taskMethod = taskMethods.parallelStream().filter(new Predicate<TaskMethodBean>() {
							
							@Override
							public boolean test(TaskMethodBean t) {
								String name = t.getName();
								return name != null && name.equals(methodName);
							}
							
						});
						if(filter_taskMethod.isParallel()) {
							Optional<TaskMethodBean> findFirst_taskMethod = filter_taskMethod.findFirst();
							if(findFirst_taskMethod != null && findFirst_taskMethod.isPresent()) {
								TaskMethodBean taskMethodBean = findFirst_taskMethod.get();
								Method method = taskMethodBean.getMethod();
								Object bean = SpringApplicationContextUtil.getContext().getBean(tcb.getCls());
								method.setAccessible(true);
								ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
								cachedThreadPool.execute(new Runnable() {
									
									@Override
									public void run() {
										runingTaskIds.add(runId.toString());
										try {
											method.invoke(bean);
										} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
											e.printStackTrace();
										} finally {
											runingTaskIds.remove(runId.toString());
										}
									}
								});
								cachedThreadPool.shutdown();
							} else {
								resMsg = "未发现指定的任务调度类的任务调度方法,请尝试重新加载任务调度类再执行!";
								resCode = -1;
							}
						} else {
							resMsg = "未发现指定的任务调度类的任务调度方法,请尝试重新加载任务调度类再执行!";
							resCode = -1;
						}
					} else {
						resMsg = "未发现指定的任务调度类,请尝试重新加载任务调度类再执行!";
						resCode = -1;
					}
				} else {
					resMsg = "未发现指定的任务调度类,请尝试重新加载任务调度类再执行!";
					resCode = -1;
				}
			} else {
				resMsg = "任务id为必传参数!";
				resCode = -1;
			}
		} catch (BeansException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} finally {
			if(writer != null) {
				result.put("resMsg" , resMsg);
				result.put("resCode" , resCode);
				writer.print(JSONObject.fromObject(result).toString());
				writer.close();
			}
		}
	}
	
	/**
	 * 加载任务调度类
	 */
	private void loadTaksClass() {
		tcbs.clear();
		ApplicationContext context = SpringApplicationContextUtil.getContext();
		Map<String , Object> tasks = context.getBeansWithAnnotation(TaskClass.class);
		Set<String> taskNames = tasks.keySet();
		Integer index = 1;
		for(String taskName : taskNames) {
			Object obj = tasks.get(taskName);
			Class<? extends Object> cls = obj.getClass();
			String clsName = cls.getName();
			if(clsName.contains("$$")) {
				try {
					cls = Thread.currentThread().getContextClassLoader().loadClass(StringUtils.substringBefore(clsName , "$$"));
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
			TaskClass tc = cls.getAnnotation(TaskClass.class);
			TaskClassBean tcb = new TaskClassBean();
			tcb.setId((index++).toString());
			tcb.setClassName(taskName);
			tcb.setCls(cls);
			if(tc != null) {
				tcb.setRemark(tc.remark());
			}
			Method [] declaredMethods = cls.getDeclaredMethods();
			for(Method method : declaredMethods) {
				Scheduled scheduled = method.getAnnotation(Scheduled.class);
				if(scheduled != null) {
					TaskMethod taskMethod = method.getAnnotation(TaskMethod.class);
					String name = method.getName();
					String remark = "";
					if(taskMethod != null) {
						remark = taskMethod.remark();
					}
					String cron = scheduled.cron();
					String zone = scheduled.zone();
					long fixedDelay = scheduled.fixedDelay();
					String fixedDelayString = scheduled.fixedDelayString();
					long fixedRate = scheduled.fixedRate();
					String fixedRateString = scheduled.fixedRateString();
					long initialDelay = scheduled.initialDelay();
					String initialDelayString = scheduled.initialDelayString();
					TaskMethodBean tm = new TaskMethodBean(cls , method , name , remark , cron , zone , fixedDelay , fixedDelayString , fixedRate , fixedRateString , initialDelay , initialDelayString);
					tcb.addTaksMethod(tm);
				}
			}
			tcbs.add(tcb);
		}
	}
	
}
